Выплатят ли по страховке или нет? Как узнать?

Описание данных

Данные были выбраны по искам страховщиков  страховой(-ым) комании(-ям) в отношении автострахования в США. В датасете представлена следующая информация по каждому иску: данные о клиенте (id, образование, пол, занятость и др.), о страховке (канал продаж, ежемесячная плата, срок действия, кол-во месяцев с прошлого требования и др.), об автомобиле, и непосредственно о требовании (размер, причина, результат и др.). Всего в датасете 25 столбцов-характеристик и 9135 строк-объектов.

Основная задача

Определить влияющие факторы и построить прогнозирующую модель результата иска (удовлетворен/не удовлетворен) для выявления клиентов, которым чаще всего удовлетворяют иски, чтобы повышать понимать, какие расходы может понести страховая компания

In [108]:
import numpy as np
import pandas as pd
import os as os
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import pydotplus
import xgboost as xgb
import eli5

from sklearn import model_selection
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.externals.six import StringIO 
from sklearn.linear_model import LogisticRegression
from IPython.display import Image 
from sklearn.tree import export_graphviz
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.ensemble import RandomForestClassifier
from sklearn import tree

warnings.filterwarnings('ignore')
sns.set_style('whitegrid')
plt.rcParams["patch.force_edgecolor"] = True
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

Импортируем данные об исках по автострахованию из датасета

In [35]:
df = pd.read_csv('Claims and Weather Sample.csv')

1. Разведывательный анализ

1.1 Знакомство с данными

In [36]:
df.head()
Out[36]:
Customer State Code State Claim Amount Response Coverage Education Effective To Date EmploymentStatus Gender ... Months Since Policy Inception Number of Open Complaints Number of Policies Policy Type Policy Claim Reason Sales Channel Total Claim Amount Vehicle Class Vehicle Size
0 BU79786 KS Kansas 276.351928 No Basic Bachelor 2/24/2011 Employed F ... 5 0 1 Corporate Auto Corporate L3 Collision Agent 384.811147 Two-Door Car Medsize
1 QZ44356 NE Nebraska 697.953590 No Extended Bachelor 1/31/2011 Unemployed F ... 42 0 8 Personal Auto Personal L3 Scratch/Dent Agent 1131.464935 Four-Door Car Medsize
2 AI49188 OK Oklahoma 1288.743165 No Premium Bachelor 2/19/2011 Employed F ... 38 0 2 Personal Auto Personal L3 Collision Agent 566.472247 Two-Door Car Medsize
3 WW63253 MO Missouri 764.586183 No Basic Bachelor 1/20/2011 Unemployed M ... 65 0 7 Corporate Auto Corporate L2 Collision Call Center 529.881344 SUV Medsize
4 HB64268 KS Kansas 281.369258 No Basic Bachelor 2/3/2011 Employed M ... 44 0 1 Personal Auto Personal L1 Collision Agent 138.130879 Four-Door Car Medsize

5 rows × 25 columns

Данные State и State Code дублируют информацию. Customer тоже не дает ничего полезного. Уберем столбцы State Code и Customer

In [38]:
df = df.drop(['Customer', 'State Code'], axis=1)
In [39]:
df.head()
Out[39]:
State Claim Amount Response Coverage Education Effective To Date EmploymentStatus Gender Income Location Code ... Months Since Policy Inception Number of Open Complaints Number of Policies Policy Type Policy Claim Reason Sales Channel Total Claim Amount Vehicle Class Vehicle Size
0 Kansas 276.351928 No Basic Bachelor 2/24/2011 Employed F 56274 Suburban ... 5 0 1 Corporate Auto Corporate L3 Collision Agent 384.811147 Two-Door Car Medsize
1 Nebraska 697.953590 No Extended Bachelor 1/31/2011 Unemployed F 0 Suburban ... 42 0 8 Personal Auto Personal L3 Scratch/Dent Agent 1131.464935 Four-Door Car Medsize
2 Oklahoma 1288.743165 No Premium Bachelor 2/19/2011 Employed F 48767 Suburban ... 38 0 2 Personal Auto Personal L3 Collision Agent 566.472247 Two-Door Car Medsize
3 Missouri 764.586183 No Basic Bachelor 1/20/2011 Unemployed M 0 Suburban ... 65 0 7 Corporate Auto Corporate L2 Collision Call Center 529.881344 SUV Medsize
4 Kansas 281.369258 No Basic Bachelor 2/3/2011 Employed M 43836 Rural ... 44 0 1 Personal Auto Personal L1 Collision Agent 138.130879 Four-Door Car Medsize

5 rows × 23 columns

In [40]:
df.tail()
Out[40]:
State Claim Amount Response Coverage Education Effective To Date EmploymentStatus Gender Income Location Code ... Months Since Policy Inception Number of Open Complaints Number of Policies Policy Type Policy Claim Reason Sales Channel Total Claim Amount Vehicle Class Vehicle Size
9129 Missouri 2340.598798 No Basic Bachelor 2/10/2011 Employed M 71941 Urban ... 89 0 2 Personal Auto Personal L1 Hail Web 198.234764 Four-Door Car Medsize
9130 Missouri 309.651122 Yes Extended College 2/12/2011 Employed F 21604 Suburban ... 28 0 1 Corporate Auto Corporate L3 Collision Branch 379.200000 Four-Door Car Medsize
9131 Missouri 816.389043 No Extended Bachelor 2/6/2011 Unemployed M 0 Suburban ... 37 3 2 Corporate Auto Corporate L2 Collision Branch 790.784983 Four-Door Car Medsize
9132 Missouri 752.444244 No Extended College 2/3/2011 Employed M 21941 Suburban ... 3 0 3 Personal Auto Personal L2 Scratch/Dent Branch 691.200000 Four-Door Car Large
9133 Missouri 261.183687 No Extended College 2/14/2011 Unemployed M 0 Suburban ... 90 0 1 Corporate Auto Corporate L3 Other Call Center 369.600000 Two-Door Car Medsize

5 rows × 23 columns

Хоть и по последнему индексу и количеству столбцов можно и так определить, все же выведем размерность датасета

In [41]:
df.shape
Out[41]:
(9134, 23)
In [42]:
df.describe()
Out[42]:
Claim Amount Income Monthly Premium Auto Months Since Last Claim Months Since Policy Inception Number of Open Complaints Number of Policies Total Claim Amount
count 9134.000000 9134.000000 9134.000000 9134.000000 9134.000000 9134.000000 9134.000000 9134.000000
mean 800.494047 37657.380009 93.219291 15.097000 48.064594 0.384388 2.966170 434.088794
std 687.096761 30379.904734 34.407967 10.073257 27.905991 0.910384 2.390182 290.500092
min 189.800768 0.000000 61.000000 0.000000 0.000000 0.000000 1.000000 0.099007
25% 399.425179 0.000000 68.000000 6.000000 24.000000 0.000000 1.000000 272.258244
50% 578.018220 33889.500000 83.000000 14.000000 48.000000 0.000000 2.000000 383.945434
75% 896.216704 62320.000000 109.000000 23.000000 71.000000 0.000000 4.000000 547.514839
max 8332.538119 99981.000000 298.000000 35.000000 99.000000 5.000000 9.000000 2893.239678

Проверим тип данных каждой переменной, все ли значения указаны, нет ли пропусков

In [43]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9134 entries, 0 to 9133
Data columns (total 23 columns):
State                            9134 non-null object
Claim Amount                     9134 non-null float64
Response                         9134 non-null object
Coverage                         9134 non-null object
Education                        9134 non-null object
Effective To Date                9134 non-null object
EmploymentStatus                 9134 non-null object
Gender                           9134 non-null object
Income                           9134 non-null int64
Location Code                    9134 non-null object
Marital Status                   9134 non-null object
Monthly Premium Auto             9134 non-null int64
Months Since Last Claim          9134 non-null int64
Months Since Policy Inception    9134 non-null int64
Number of Open Complaints        9134 non-null int64
Number of Policies               9134 non-null int64
Policy Type                      9134 non-null object
Policy                           9134 non-null object
Claim Reason                     9134 non-null object
Sales Channel                    9134 non-null object
Total Claim Amount               9134 non-null float64
Vehicle Class                    9134 non-null object
Vehicle Size                     9134 non-null object
dtypes: float64(2), int64(6), object(15)
memory usage: 1.6+ MB

Интерпретируем каждую переменную, чтобы быть в курсе дела

  • Customer - номер клиента
  • State - штат (Nebarska, Kansas, Oklahoma, Iowa, Missouri)
  • Claim Amount - сумма иска
  • Response - ответ страховой компании
    • Yes
    • No
  • Coverage - вид покрытия ущерба
    • Basic
    • Extended
    • Premium
  • Education - образование
    • High School or Below
    • College
    • Bachelor
    • Master
  • Effective To Date - срок действия страховки
  • EmployedStatus - вид занятости
    • Employed - занятый
    • Unemployed - безработный
    • Medical Leave - на больничном
    • Retired - в отставке
    • Disabled - инвалид
  • Gender - пол
  • Income - годовой доход в долл. США
  • Location Code - код месторасположения
    • Rural
    • Suburban
    • Urban
  • Marital Status - семейное положение
  • Monthly Premium Auto - ежемесячный платеж по автостраховке
  • Months Since Last Claim - месяцев с последнего иска
  • Months Since Policy Inception - месяцев с начала действия полиса автострахования
  • Number of Open Complaints - количество открытых жалоб
  • Number of Policies - количество полисов
  • Policy Type - тип полиса
    • Corporate Auto
    • Personal Auto
    • Special Auto
  • Policy - полис (L1, L2, L3 на каждый тип полиса)
  • Claim Reason - причина иска
    • Collision - ДТП
    • Hail - град
    • Scratch/Dent - царапина/вмятина
    • Other
  • Sales Channel - канал продаж
  • Total Claim Amount - итоговая сумма иска
  • Vehicle Class - класс ТС (6 типов)
  • Vehicle Size - размер ТС (3 типа)

1.2 Построим и рассмотрим графики распределений количественных переменных и проверим, есть ли связь между построенными количественными переменными и видом занятости истца

In [19]:
cols = ['Claim Amount', 'Total Claim Amount', 'Income', 'Monthly Premium Auto', 'EmploymentStatus', 'Gender',
                                                                                        'Response','Marital Status']
sns.pairplot(df[cols], hue='EmploymentStatus', palette='Set1', size=6)
Out[19]:
<seaborn.axisgrid.PairGrid at 0x112c5b518>
  • По графику распределения "суммы иска" можно сделать вывод, что доля исков на сумму < 1000 долл США очень велика
  • По графику распределения "итоговой суммы иска" можно заметить, что итоговая сумма иска у большинства истцов не превышает 600 долл США
  • По графику распределения "доходов" истцов видно, что больше всего истцов с нулевым доходом

  • Возможно наличие связи между "Суммой иска" и "Ежемесячный платеж по автостраховке"; "Итоговой суммой иска" и "Ежемесячнаый платеж по автостраховке", и логично было бы предположить связь между "Суммой иска" и "Итоговой суммой иска"

  • По цветовой гамме можно определить, что у занятые иско-заявителе при большем доходе платят те же ежемесячные суммы, что и остальные
In [12]:
print("График распределения Claim Amount:", round(df[df['Claim Amount'] < 1000].shape[0]/df.shape[0]*100, 2), '% исков с суммой требования < 1000$')
print("График распределения Total Claim Amount:", round(df[df['Total Claim Amount'] < 600].shape[0]/df.shape[0]*100, 2), '% исков, где итоговая исковая сумма составляет < 600$')
print("График распределения Income:", round(df[df['Income'] == 0].shape[0]/df.shape[0]*100, 2), '% исков от клиентов с доходом равным 0$')
График распределения Claim Amount: 79.35 % исков с суммой требования < 1000$
График распределения Total Claim Amount: 80.04 % исков, где итоговая исковая сумма составляет < 600$
График распределения Income: 25.37 % исков от клиентов с доходом равным 0$

Осталось еще пара менее важных количественных переменных, посмотрим, что можно на их графике увидеть

In [13]:
sns.distplot(df['Months Since Last Claim'], kde=False, bins=20)
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x1079e7668>
In [14]:
sns.distplot(df['Months Since Policy Inception'], kde=False, bins=20)
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x112104748>
  • По графику Months Since Last Claim четко видно, что с увеличением переменной количество иско-заявителей становится меньше
  • По графику Months Since Policy Inception можно наблюдать, что данная переменная практически не влияет (или влияет очень слабо) на количсетсво иско-заявителей
In [20]:
cols2 = ['Claim Amount', 'Total Claim Amount', 'Income', 'Monthly Premium Auto', 'EmploymentStatus', 'Gender',
                                                                                        'Response','Marital Status']
sns.pairplot(df[cols2], hue='Response', palette='Set1', size=5)
Out[20]:
<seaborn.axisgrid.PairGrid at 0x1a23547438>

1.4 Визуализируем матрицу корреляций

Перед этим выдвенем гипотезы по графикам выше и согласно логическим рассуждениям:

  • Между Claim Amount и Months Premium Auto прослеживается положительная связь (средняя)
  • Между Total Claim Amount и Months Premium Auto прослеживается положительная связь (средняя)
  • Между Claim Amount и Total Claim Amount прослеживается положительная связь (слабая)
  • Чем больше Monthly Premium Auto и Total Claim Amount, тем меньше Response = Yes
In [21]:
sns.set(font_scale=1.5)
matr_corr = sns.heatmap(df[cols].corr(),
                 cbar=True,
                 annot=True)

2. Модели

Переведем качественные переменные из текстого в цифровой вид

In [22]:
Y_feature = df['Response'].apply(lambda x: 0 if x == 'No' else 1)
Y_feature
Out[22]:
0       0
1       0
2       0
3       0
4       0
5       1
6       1
7       0
8       1
9       0
10      0
11      0
12      0
13      1
14      0
15      0
16      0
17      0
18      0
19      0
20      0
21      0
22      1
23      0
24      0
25      0
26      0
27      0
28      0
29      0
       ..
9104    0
9105    0
9106    0
9107    1
9108    0
9109    0
9110    1
9111    0
9112    0
9113    0
9114    0
9115    0
9116    0
9117    0
9118    0
9119    1
9120    0
9121    0
9122    1
9123    1
9124    0
9125    0
9126    0
9127    0
9128    0
9129    0
9130    1
9131    0
9132    0
9133    0
Name: Response, Length: 9134, dtype: int64
In [23]:
X = df.drop(['Customer', 'Response'], 1)
X_numeric = X.select_dtypes(include='number')
X_object = X.select_dtypes(exclude='number')
In [24]:
X_object['Effective To Date'] = pd.to_datetime(pd.to_datetime(X_object['Effective To Date'], 
                                                             infer_datetime_format = True))
X_object['today'] = pd.to_datetime('2011-09-03')
X_numeric['Days until expire'] = (X_object['today'] - X_object['Effective To Date']).dt.days
X_object = X_object.drop(['today', 'Effective To Date'],1)
In [25]:
X_object = pd.get_dummies(X_object)
X_object.shape
Out[25]:
(9134, 55)
In [26]:
X_features = pd.concat([X_numeric, X_object], axis=1)
X_features.shape
Out[26]:
(9134, 64)

Разделим выборку на обучающую и тестовую

In [27]:
X_train, X_test, y_train, y_test = train_test_split(X_features, Y_feature, test_size=0.3, random_state=5)

2.1 Логистическая регрессия (без регуляризации)

In [28]:
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
logreg.score(X_train, y_train), logreg.score(X_test, y_test)
Out[28]:
(0.8604723916783983, 0.8482305727836555)
In [29]:
cv_logreg = cross_val_score(LogisticRegression(), X_train, y_train, cv=3)
print(cv_logreg,'\n', cv_logreg.mean())
[0.86022514 0.86062881 0.86056338] 
 0.8604724445861988

2.2 Логистическая регрессия (с регуляризацией)

In [30]:
logreg2 = LogisticRegression(C=5)
logreg2.fit(X_train, y_train)
logreg2.score(X_train, y_train), logreg2.score(X_test, y_test)
Out[30]:
(0.8604723916783983, 0.8482305727836555)
In [31]:
cv_logreg2 = cross_val_score(logreg2, X_train, y_train, cv=3)
print(cv_logreg2,'\n', cv_logreg2.mean())
[0.86022514 0.86062881 0.86056338] 
 0.8604724445861988

Такое же значение, как и у кросс-валидации логрегрессии без регуляризации

2.3 Дерево решений

In [32]:
dt_params = {
    'criterion': ['gini', 'entropy'],
    'max_depth': [3,5,7],
    'min_samples_split': [3,5,7]
}
In [72]:
dt = DecisionTreeClassifier()
cv_dt = GridSearchCV(dt, dt_params, cv=5, scoring='f1')
cv_dt.fit(X_train, y_train)
Out[72]:
GridSearchCV(cv=5, error_score='raise',
       estimator=DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best'),
       fit_params=None, iid=True, n_jobs=1,
       param_grid={'criterion': ['gini', 'entropy'], 'max_depth': [3, 5, 7], 'min_samples_split': [3, 5, 7]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring='f1', verbose=0)
In [73]:
cv_dt.best_estimator_
Out[73]:
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=7,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=7,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')
In [74]:
cv_dt.best_score_
Out[74]:
0.461662923034532
In [76]:
preds = cv_dt.predict(X_test)

print(classification_report(y_test, preds))
             precision    recall  f1-score   support

          0       0.89      0.98      0.93      2325
          1       0.71      0.32      0.44       416

avg / total       0.86      0.88      0.86      2741

In [77]:
print(confusion_matrix(y_test, preds))
[[2269   56]
 [ 281  135]]
In [78]:
pd.Series(y_test).value_counts()
Out[78]:
0    2325
1     416
Name: Response, dtype: int64
In [80]:
dot_data = tree.export_graphviz(cv_dt.best_estimator_, out_file=None, 
feature_names=X_features.columns) 

graph = pydotplus.graph_from_dot_data(dot_data) 

Image(graph.create_png())
Out[80]:
  • Согласно данному дереву решений, 1-м важным факторов является инвалидность клиента, 2-м фактор - причина иска вмятина или другое
In [118]:
# Можно сохранить дерево себе на память и разглядеть получше
graph.write_png('tree.png')
Out[118]:
True

2.4 Голосование

In [81]:
voting = VotingClassifier(estimators=[('lr', logreg), ('dt', dt), ('lr2', logreg2)], voting='hard')
voting.fit(X_train,y_train)
voting.score(X_train,y_train), voting.score(X_test,y_test)
Out[81]:
(0.8604723916783983, 0.8482305727836555)
In [82]:
cv_voting = cross_val_score(voting, X_train, y_train, cv=3)
print(cv_voting,'\n', cv_voting.mean())
[0.86022514 0.86062881 0.86056338] 
 0.8604724445861988

Значение чуть больше, чем у логрегрессии

2.5 Бэггинг

In [102]:
bg = BaggingClassifier(base_estimator=dt, n_estimators=100, random_state=18)
bg.fit(X_train,y_train)
bg.score(X_train,y_train), bg.score(X_test,y_test)
Out[102]:
(1.0, 0.9919737322145202)
In [103]:
cv_bg = cross_val_score(bg, X_train, y_train, cv=3, scoring = "f1_macro")
print(cv_bg,'\n', cv_bg.mean())
[0.96197339 0.96280846 0.93307905] 
 0.9526203002135015

Значение стало очень большим после того, как я заменил базу оценки с логрегрессии на дерево решений

2.4 Случайный лес

In [85]:
X_train, X_test, y_train, y_test = train_test_split(X_features, Y_feature, test_size=0.3, random_state=18)

rf_params = {
    'criterion': ['gini', 'entropy'],
    'max_depth': [3,5,7],
    'min_samples_split': [3,5,7],
    'n_estimators': [100,300,500]
}

cv_rf = GridSearchCV(RandomForestClassifier(), rf_params, cv=5, scoring='f1', n_jobs=-1)
cv_rf.fit(X_train, y_train)
Out[85]:
GridSearchCV(cv=5, error_score='raise',
       estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=None, verbose=0,
            warm_start=False),
       fit_params=None, iid=True, n_jobs=-1,
       param_grid={'criterion': ['gini', 'entropy'], 'max_depth': [3, 5, 7], 'min_samples_split': [3, 5, 7], 'n_estimators': [100, 300, 500]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring='f1', verbose=0)
In [86]:
preds_rf = cv_rf.predict(X_test)
print(classification_report(y_test, preds_rf))
             precision    recall  f1-score   support

          0       0.87      1.00      0.93      2320
          1       0.93      0.17      0.28       421

avg / total       0.88      0.87      0.83      2741

In [87]:
print(confusion_matrix(y_test, preds_rf))
[[2315    5]
 [ 351   70]]

2.5 XGBoost

In [88]:
X_train, X_test, y_train, y_test = train_test_split(X_features, Y_feature, test_size=0.3, random_state=18)

xgb_params = {
    'max_depth': [3,5,7],
    'n_estimators': [100,300,500],
    'learning_rate': [0.01, 0.05, 0.1]
}

cv_xgb = GridSearchCV(xgb.XGBClassifier(), xgb_params, cv=5, scoring='f1', n_jobs=-1)
cv_xgb.fit(X_train, y_train)
Out[88]:
GridSearchCV(cv=5, error_score='raise',
       estimator=XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
       colsample_bytree=1, gamma=0, learning_rate=0.1, max_delta_step=0,
       max_depth=3, min_child_weight=1, missing=None, n_estimators=100,
       n_jobs=1, nthread=None, objective='binary:logistic', random_state=0,
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1),
       fit_params=None, iid=True, n_jobs=-1,
       param_grid={'max_depth': [3, 5, 7], 'n_estimators': [100, 300, 500], 'learning_rate': [0.01, 0.05, 0.1]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring='f1', verbose=0)
In [89]:
preds_xgb = cv_xgb.predict(X_test)
print(classification_report(y_test, preds_xgb))
             precision    recall  f1-score   support

          0       1.00      0.99      1.00      2320
          1       0.97      1.00      0.99       421

avg / total       1.00      1.00      1.00      2741

In [90]:
print(confusion_matrix(y_test, preds_xgb))
[[2308   12]
 [   0  421]]

Здесь серьезное заявление на самую эффективную модель

2.6 ExtraTrees

In [95]:
et = ExtraTreesClassifier(n_estimators=100, max_features=5)
et.fit(X_train, y_train)
et.score(X_train, y_train), et.score(X_test, y_test)
Out[95]:
(1.0, 0.9875957679678949)
In [96]:
cv_et = cross_val_score(et, X_train, y_train, cv=3)
print(cv_et,'\n', cv_et.mean())
[0.96763602 0.96386673 0.95821596] 
 0.963239571396829

Очень хорошая эффективность, но не самая высокая

2.7 Gradient Boosting

In [98]:
gb = GradientBoostingClassifier(n_estimators=100)
gb.fit(X_train, y_train)
gb.score(X_train, y_train), gb.score(X_test, y_test)
Out[98]:
(0.8942593461598624, 0.874863188617293)
In [101]:
cv_gb = cross_val_score(gb, X_train, y_train, cv=3)
print(cv_gb,'\n', cv_gb.mean())
[0.88461538 0.89441577 0.89061033] 
 0.8898804934997689

Не самая высокая оценка

Выводы:

  • Модель XGBoost оправдала свою распространенность и известность, оказавшись модель с лучшей оценкой эффективности
  • Данную модель можно улучшить, поиграв с переменными, которых очень много, что я планирую сделать в обозримом удущем
  • По большинству удовлетворенных исков компенсируется не полная сумма, в основном это не более 600 долл США